package matse11.LSpielProject;


import java.util.*;

public class Computer extends Spieler{

	//Speichert die Positionen zum loesen 
	private ArrayList<short[][][]> loeslist=new ArrayList<short[][][]>();
	
	//ArrayList fuer alle moeglichen setzungsmoegl.
	public ArrayList<short[][]> alist=new ArrayList<short[][]>();
	
	
	
	//Besetzen der Attribute von Spieler Konstruktor(MOMENTAN DEFAULT, da noch keine Vererbung)
	Computer( )
	{
		super("Computer");
		this.pIsComputer = true;
		//Es werden Dreidimensionale Arrays erzeugt mit allen Gewinnoptionen und Gewinn-
		//moeglichen Positionen
		
		short[][][] positionen0=new short[15][4][4];
		
		short[][]feld111={{0,0,-1,0},
				{2,2,2,0},
				{1,0,2,0},
				{1,1,1,-1}};
		positionen0[0]=feld111;
		short[][]feld211={{0,0,2,0},
				{-1,0,2,0},
				{1,2,2,-1},
				{1,1,1,0}};
		positionen0[1]=feld211;
		short[][]feld311={{0,-1,0,0},
				{-1,2,0,0},
				{1,2,2,2},
				{1,1,1,0}};
		positionen0[2]=feld311;
		short[][]feld411={{0,-1,0,0},
				{-1,0,0,2},
				{1,2,2,2},
				{1,1,1,0}};
		positionen0[3]=feld411;
		short[][]feld511={{1,0,-1,0},
				{1,1,1,-1},
				{2,2,2,0},
				{0,0,2,0}};
		positionen0[4]=feld511;
		short[][]feld611={{0,0,0,-1},
				{2,2,2,0},
				{1,0,2,0},
				{1,1,1,-1}};
		positionen0[5]=feld611;
		short[][]feld711={{0,0,2,0},
				{-1,0,2,0},
				{1,2,2,0},
				{1,1,1,-1}};
		positionen0[6]=feld711;
		short[][]feld811={{0,0,-1,0},
				{-1,2,0,0},
				{1,2,2,2},
				{1,1,1,0}};
		positionen0[7]=feld811;
		short[][]feld911={{0,0,-1,0},
				{-1,0,0,2},
				{1,2,2,2},
				{1,1,1,0}};
		positionen0[8]=feld911;
		short[][]feld1011={{1,0,-1,0},
				{1,1,1,2},
				{-1,2,2,2},
				{0,0,0,0}};
		positionen0[9]=feld1011;
		short[][]feld1111={{0,0,0,0},
				{2,2,2,-1},
				{1,0,2,0},
				{1,1,1,-1}};
		positionen0[10]=feld1111;
		short[][]feld1211={{0,0,2,0},
				{2,2,2,0},
				{1,0,-1,0},
				{1,1,1,-1}};
		positionen0[11]=feld1211;
		short[][]feld1311={{0,2,0,0},
				{-1,2,0,0},
				{1,2,2,-1},
				{1,1,1,0}};
		positionen0[12]=feld1311;
		short[][]feld1411={{0,0,2,0},
				{-1,0,2,0},
				{1,-1,2,2},
				{1,1,1,0}};
		positionen0[13]=feld1411;
		short[][]feld1511={{0,0,2,0},
				{0,-1,2,0},
				{-1,1,2,2},
				{0,1,1,1}};
		positionen0[14]=feld1511;
		
		
	  
		short[][][] positionen1=new short[3][4][4];
		short[][][] positionen2=new short[3][4][4];
		short[][][] positionen3=new short[5][4][4];
		short[][][] positionen4=new short[3][4][4];
		
		//1Schritt zum Sieg
		short[][]feld0={{0,2,0,0},
						 {-1,2,0,0},
						 {1,2,2,0},
						 {1,1,1,-1}};
		positionen1[0]=feld0;
		short[][]feld1={{0,2,0,0},
						 {0,2,0,-1},
						 {1,2,2,-1},
						 {1,1,1,0}};
		positionen1[1]=feld1;
		short[][]feld2={{1,0,-1,0},
						 {1,1,1,-1},
						 {2,2,2,0},
						 {2,0,0,0}};
		positionen1[2]=feld2;
		
		//2Schritte zum Sieg
		short[][]feld3={{-1,2,0,0},
						 {0,2,0,0},
						 {1,2,2,-1},
						 {1,1,1,0}};
		positionen2[0]=feld3;
		short[][]feld4={{0,2,-1,0},
						 {0,2,0,0},
						 {1,2,2,-1},
						 {1,1,1,0}};
		positionen2[1]=feld4;
		short[][]feld5={{0,0,0,0},
						 {2,2,2,0},
						 {-1,1,2,-1},
						 {0,1,1,1}};
		positionen2[2]=feld5;
		
		//3Schritte bis zum Sieg
		short[][]feld6={{-1,2,0,0},
						 {0,2,0,0},
						 {1,2,2,-1},
						 {1,1,1,0}};
		positionen3[0]=feld6;
		short[][]feld7={{0,2,-1,0},
						 {0,2,0,0},
						 {1,2,2,0},
						 {1,1,1,-1}};
		positionen3[1]=feld7;
		short[][]feld8={{0,0,0,0},
					 	 {2,2,2,0},
				   		 {1,0,2,-1},
						 {1,1,1,-1}};
		positionen3[2]=feld8;
		short[][]feld9={{0,2,0,0},
						 {0,2,0,-1},
						 {1,2,2,0},
						 {1,1,1,-1}};
		positionen3[3]=feld9;
		short[][]feld10={{0,2,0,0},
				 		 {0,2,0,0},
				 		 {1,2,2,-1},
						 {1,1,1,-1}};
		positionen3[4]=feld10;
		
		//4Schritte bis zum Sieg
		short[][]feld11={{0,2,-1,0},
						 {0,2,0,-1},
						 {1,2,2,0},
						 {1,1,1,0}};
		positionen4[0]=feld11;
		short[][]feld12={{0,0,-1,0},
						 {2,2,2,0},
						 {2,1,-1,0},
						 {0,1,1,1}};
		positionen4[1]=feld12;
		short[][]feld13={{0,0,0,-1},
						{2,2,2,0},
						{2,1,-1,0},
						{0,1,1,1}};
		positionen4[2]=feld13;

		//all diese Moeglichkeiten werden in der Arrayliste Loeslist gespeichert,
		//die als Attribut von Computer deklariert ist
		this.loeslist.add(positionen0);
		this.loeslist.add(positionen1);
		this.loeslist.add(positionen2);
		this.loeslist.add(positionen3);
		this.loeslist.add(positionen4);
		
		
		
		
		//		super("Comp")
	}
	
	
	//einfuegen in arrayliste
	public void listein(short[][] a){
		this.alist.add(a);
	}
	
	/*
	 * Methode zur Ausgabe einer Arrayliste, diente zur Kontrolle,
	 *  ob dass ergebnis richtig ist
	//ausgeben aus arraylist*/
	public short[][] listaus(int i){
		return this.alist.get(i);
	}
	
	//uebertragen der arraywerte von einem in ein Neues
	public short[][] arcopy(short[][] ak)
	{
		short [][] a=new short [ak.length][ak[0].length];
		for(int i=0; i<4; i++){
			for(int e=0; e<4; e++)
			{
				a[i][e]=ak[i][e];
			}
		}
		return a;
	}
	
	//Diese Methode listet alle Moeglichkeiten auf, wie der Computer den L-Stein setzen kann
	public void setAlist(short afeld[][])
	{
		//Aliste leeren
		this.alist=new ArrayList<short[][]>();
		
		//Mit den moeglichen schritten befuellen
		//ArrayList HORIZONTAL befuellen
				for(int i=0; i<4; i++)
				{
					
					//pruefen ob 3 in einer zeile == 0
					for(int e=0; e<2; e++)
					{
						if(afeld[i][e]==0)
						{
						   if (afeld[i][e+1]==0)
						   {
							   if (afeld[i][e+2]==0)
							   {
								 //oberste Reihe
								 if(i==0)
								 {
							     // dann neues array einfuegen
								  if(afeld[i+1][e]==0)
								  {
									  //000
									  //0
									  short[][] a=this.arcopy(afeld);
									  a[i][e]=2;
									  a[i][e+1]=2;
									  a[i][e+2]=2;
									  a[i+1][e]=2;
									  this.listein(a);
								  }
								  if(afeld[i+1][e+2]==0)
								  {
									  //000
									  //  0
									  short[][] a=this.arcopy(afeld);
									  a[i][e]=2;
									  a[i][e+1]=2;
									  a[i][e+2]=2;
									  a[i+1][e+2]=2;
									  this.listein(a);
								  }
								 }
								 //unterste Reihe
								 else if(i==3)
								 {
									 if(afeld[i-1][e]==0)
									  {
										 //0
										 //000
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i-1][e]=2;
										  this.listein(a);
									  }
									  if(afeld[i-1][e+2]==0)
									  {
										  //  0
										  //000
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i-1][e+2]=2;
										  this.listein(a);
									  }

								 }
								 //die anderen
								
								 else
								 {
									 if(afeld[i-1][e]==0)
									  {
										 //0
										 //000
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i-1][e]=2;
										  this.listein(a);
									  }
									 
									  if(afeld[i-1][e+2]==0)
									  {
										  //  0
										  //000
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i-1][e+2]=2;
										  this.listein(a);
									  }
									  if(afeld[i+1][e]==0)
									  {
										  //000
										  //0
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i+1][e]=2;
										  this.listein(a);
									  }
									  if(afeld[i+1][e+2]==0)
									  {
										  //000
										  //  0
										  short[][] a=this.arcopy(afeld);
										  a[i][e]=2;
										  a[i][e+1]=2;
										  a[i][e+2]=2;
										  a[i+1][e+2]=2;
										  this.listein(a);
									  }
								 }
							   }
						   }
						}
					}
				}
				
				
				//Alist VERTIKAL mit den moeglichen Schritten befuelen
				for(int i=0; i<4; i++)
				{
					//pruefen ob 3 in einer zeile == 0
					for(int e=0; e<2; e++)
					{
						if(afeld[e][i]==0)
						{
						   if (afeld[e+1][i]==0)
						   {
							   if (afeld[e+2][i]==0)
							   {
								   //erste Spalte
									 if(i==0)
									 {
								      // dann neues array einfuegen
									  if(afeld[e][i+1]==0)
									  {
										  //00
										  //0
										  //0
										  short[][] a=this.arcopy(afeld);
										  a[e][i]=2;
										  a[e+1][i]=2;
										  a[e+2][i]=2;
										  a[e][i+1]=2;
										  this.listein(a);
									  }
									  if(afeld[e+2][i+1]==0)
									  {
										  //0
										  //0
										  //00
										  short[][] a=this.arcopy(afeld);
										  a[e][i]=2;
										  a[e+1][i]=2;
										  a[e+2][i]=2;
										  a[e+2][i+1]=2;
										  this.listein(a);
									  }
									 }
									  //letzte Spalte
									 else if(i==3)
									 {
										 if(afeld[e][i-1]==0)
										  {
											 //00
											 // 0
											 // 0
											  short[][] a=this.arcopy(afeld);
											  a[e][i]=2;
											  a[e+1][i]=2;
											  a[e+2][i]=2;
											  a[e][i-1]=2;
											  this.listein(a);
										  }
										  if(afeld[e+2][i-1]==0)
										  {
											  // 0
											  // 0
											  //00
											  short[][] a=this.arcopy(afeld);
											  a[e][i]=2;
											  a[e+1][i]=2;
											  a[e+2][i]=2;
											  a[e+2][i-1]=2;
											  this.listein(a);
										  }

										}
										 //die anderen
									
									 else
									 {
										 if(afeld[e][i-1]==0)
										  {
											 //00
											 // 0
											 // 0
											  short[][] a=this.arcopy(afeld);
											  a[e][i]=2;
											  a[e+1][i]=2;
											  a[e+2][i]=2;
											  a[e][i-1]=2;
											  this.listein(a);
										  }
										  if(afeld[e+2][i-1]==0)
										  {
											  // 0
											  // 0
											  //00
											  short[][] a=this.arcopy(afeld);
											  a[e][i]=2;
											  a[e+1][i]=2;
											  a[e+2][i]=2;
											  a[e+2][i-1]=2;
											  this.listein(a);
										  }
									 }
						        }
						   }
						}		  
					}
				}
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	//Methode zum Suchen ob GewinnendenPosition erreicht werden kann
	//falls nicht wird eine anderen ERLAUBTEN Positionen zufaellig genommen
	public short[][] Win(){
		
		for(int i=0; i<this.alist.size(); i++)
		{
			for(int e=0; e<this.loeslist.size(); e++)
			{
				for(int u=0; u<this.loeslist.get((e)).length; u++)
				{
					//aus der Liste der Loesungsarrays wird jedes feld einzeln ausgelesen
					//es wird 4mal gedreht und 2mal gespiegelt, damit man jede erdenkliche
					//dreh/spiegel situation ueberprueft
					//falls er eine situation gefunden hat in die er gehen kann (mit Gewinn-
					//Compare()) wird sie ausgewaehlt
					short[][] gedreht=this.loeslist.get(e)[u];
					
					for(int dreh=0; dreh<4; dreh++)
					{
						gedreht=this.feldDrehen(gedreht);
						
						if(this.gewinnCompare(this.alist.get(i), gedreht))
						{
							
							return gedreht;	
						}
					}
					//nachdem das Feld um 360grad gedreht wurde wird es gespiegelt
					gedreht=this.feldSpiegel(gedreht);
					for(int dus=0; dus<4; dus++)
					{
						
						gedreht=this.feldDrehen(gedreht);
						
						if(this.gewinnCompare(this.alist.get(i), gedreht))
						{
							
							return gedreht;	
						}
					}
					//nachdem das gespiegelte Feld um 360grad gedreht wurde wird es zurueck
					//gespiegelt um den ausgangszustand zu erhalten
					gedreht=this.feldSpiegel(gedreht);
				}
			}
		}
		
		//wenn bisher nichts returned wurde -->
		//keine gewinnloesung moeglich, also von allen verblebenden zufallszug
		int wahl=(int) (this.alist.size()*Math.random());
		//ausgabe des zufallsspielzugs
		return this.alist.get(wahl);
		
		}
	
	
	//compare-Methode, prueft ob nur ein neutraler Stein noch zur Gewinnposition versetzt
	//werden muss
	public boolean gewinnCompare(short[][] a, short[][] b)
	{
		//Misst wie viele Unterschiede im Feld sind
		int abweichung=0;
		//Summe der Abweichung: 
		int sda=0;
		
		for(int i=0; i<4; i++)
		{
			for(int e=0; e<4; e++)
			{
				if(a[i][e]!=b[i][e])	
				{
					abweichung=abweichung+1;
					sda=sda+a[i][e];
				}
			}
		}
		
		//nur neutraler stein muesste veraendert werden
				if(abweichung==0 || (abweichung==2 && sda==(-1)))
				{
					return true;
				}
				//nicht zu erreichen
				else
				{
					return false;
				}
		}
	
		
	public int loseCompare(short[][] a, short[][] b)
	{
		//Misst wie viele Unterschiede im Feld sind
		int abweichung=0;
		//Summe der Abweichung: 
		int sda=0;
		
		short[][] aa=new short[4][4];
		short[][] bb=new short[4][4];
		
		aa=this.arcopy(a);
		bb=this.arcopy(b);
		
		for(int i=0; i<4; i++)
		{
			for(int e=0; e<4; e++)
			{
				//blau aus a und b loeschen
				if(a[i][e]==1)
				{
					aa[i][e]=0;
				}
				if(b[i][e]==1)
				{
					bb[i][e]=0;
				}
				
				if(aa[i][e]!=bb[i][e])	
				{
					abweichung=abweichung+1;
					sda=sda+a[i][e];
				}
			}
		}
		
		//falls nur in einem neutralen stein ein unterschied liegt muss der zweite 
		//entsprechend verschoben werden
		if(abweichung==2 && sda==(-1))
		{
			return -1;
		}
		//falls keine abweichung vorliegt und nur blau verschoben werden muss
		//muss entfernt werden die moeglichkeit
		else if(abweichung==0)
		{
			return 1;
		}
		
		else 
		{
			return 0;
		}
	}
	
	
	//Methode zum feststellen ob neutrale steine untersch. pos haben eines neutralen Steines
	public boolean stver(short[][] a, short[][] b)
	{
		for(int i=0; i<4; i++)
		{
			for(int e=0; e<4; e++)
			{
				if(a[e][i]==(-1) && b[e][i]==(-1))
				{
					return false;
				}
			}
		}
		return true;
		
	}
	
	
	
	//compare-Methode, prueft ob der Inhalt zweier Arrays identisch ist
	public boolean arrayCompare(short[][] a, short[][] b)
	{
		
		for(int i=0; i<4; i++)
		{
			for(int e=0; e<4; e++)
			{
				if(a[i][e]!=b[i][e])	
				{
					return false;
				}
					
			}
		}	
		return true;
	}
	
	/*
	 * Die Methode ist dafuer da, damit einerseits die Option dass der L-Stein
	 * nicht verschoben wird loescht, und die aufpasst, dass der PC nicht eine Position auswaehlt,
	 * durch die er in eine Verlier Situation kommen kann.
	 * Sie veraendert daher die Liste der moeglichen Zuege.
	 */
	public void verboteneLoeschen(short[][] old)
	{
		int laenge=this.alist.size();
		
		//zum loeschen des alten feldes aus der liste der moeglichen zuege
		for(int i=0; i<laenge; i++)
		{
			if(this.arrayCompare(old, this.alist.get(i)))
			{
				this.alist.remove(i);
				break;
			}
		}
		
		
		
		//NEUE METHODE, Win Compare umdrehen!!
		//falls der PC (ROT = 2) Die Situation von Spieler(BLAU=1) einnehmen wuerde und nur 1
		//neutraler stein noch verschoben werden muesste, dann muss dieser moeglicher  zug geloescht werden
		
		for(int i=0; i<this.alist.size(); i++)
		{ 	boolean win=false;
			for(int e=0; e<this.loeslist.size(); e++)
			{
				for(int u=0; u<this.loeslist.get((e)).length; u++)
				{
					//aus der Liste der Loesungsarrays wird jedes feld einzeln ausgelesen
					//es wird 4mal gedreht und 2mal gespiegelt, damit man jede erdenkliche
					//dreh/spiegel situation ueberprueft
					//falls er eine situation gefunden hat in die er gehen kann (mit Gewinn-
					//Compare()) wird sie ausgewaehlt
					short[][] gedreht=this.invertiere(this.arcopy(this.loeslist.get(e)[u]));
					
					for(int dreh=0; dreh<4; dreh++)
					{
						gedreht=this.feldDrehen(gedreht);
						
						if(this.arrayCompare(this.alist.get(i), gedreht))
						{
							if(this.alist.size() != 1)
							{
							this.alist.remove(i);
							i--;
							win=true;
							}
						}
						
					}
					if(!win)
					{
					//nachdem das Feld um 360grad gedreht wurde wird es gespiegelt
					gedreht=this.feldSpiegel(gedreht);
					for(int dus=0; dus<4; dus++)
					{
						
						gedreht=this.feldDrehen(gedreht);
						
						if(this.arrayCompare(this.alist.get(i), gedreht))
						{
							if(this.alist.size() != 1)
							{
							this.alist.remove(i);
							i--;
							win=true;
							}
						}
					}
					//nachdem das gespiegelte Feld um 360grad gedreht wurde wird es zurueck
					//gespiegelt um den ausgangszustand zu erhalten
					gedreht=this.feldSpiegel(gedreht);
					}
					if(win)
					{
						break;
					}
				}
				
				if(win)
				{
					break;
				}
			}
		}

	}
	
	
	public short[][] spielzug(short[][] ak)
	{
		
		short[][] afeld=this.arcopy(ak);
		short[][] oldfeld=this.arcopy(ak);
		
		
		//ersetzen der eigenen elemente durch 0 
		for(int i=0; i<4; i++){
			for(int e=0; e<4; e++){
				if(afeld[i][e]==2)
				{
				  afeld[i][e]=0;
				}
			}
		}
		
		//befuellen der arrayliste, um alle moeglichen positionen zu haben
		this.setAlist(afeld);

		
		//anfangsfeld loeschen und andere veraendern
		this.verboteneLoeschen(oldfeld);
		
		//Zufallsspielzug oder falls moeglich win spiel
		return this.Win();
	}
	
	//nur zum testen und ausgeben!!!
	public String ausgabe(short[][] ak){
		String aus="";
		for(int i=0; i<4; i++){
			for(int e=0; e<4; e++){
		     aus=aus+" "+ak[i][e];		
			}
			aus=aus+"\n";
		}
		return aus;
	}
	
	public short [][] feldDrehen(short[][] feld) {
		short [][]tmp=new short[4][4];
//		erzeugen eines temporaeren feldes, damit ich das zurueckgeben kann
		for(int  l=0;l<tmp.length;l++){
			for(int k=0;k<tmp[0].length;k++){
				tmp[tmp.length-1-k][l]=feld[l][k];
			}
		}
		return tmp;
	}
	
	public short [][] feldSpiegel(short[][]feld){
		short [][]tmp=new short[4][4];
		for(int i=0;i<feld.length;i++){
			for(int j=0;j<feld[0].length;j++){
				tmp[feld.length-1-i][j]=feld[i][j];
			}
		}
		return tmp;
	}
	
	public short[][] invertiere(short[][] feld){
		for(int i=0; i<feld.length;i++){
			for(int j=0; j<feld.length; j++){
				if(feld[i][j]==1){
					feld[i][j]=2;
				}
				else if(feld[i][j]==2){
					feld[i][j]=1;
				}
			}
		}
		return feld;
	}
}
